package com.jsoft.jsofted.framework.service.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.diboot.core.mapper.BaseCrudMapper;
import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.vo.Pagination;
import com.jsoft.jsofted.framework.entity.IdEntity;
import com.jsoft.jsofted.framework.entity.SortEntity;
import com.jsoft.jsofted.framework.entity.TimeEntity;
import com.jsoft.jsofted.framework.entity.TreeEntity;
import com.jsoft.jsofted.framework.mybatis.CocQueryWrapper;
import com.jsoft.jsofted.framework.service.CocService;
import com.jsoft.jsofted.framework.utils.CocException;
import com.jsoft.jsofted.framework.utils.ObjectUtils;
import com.jsoft.jsofted.framework.utils.PageResult;
import com.jsoft.jsofted.framework.vo.TreeMaker;
import com.jsoft.jsofted.framework.vo.TreeNode;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.Consumer;

public abstract class CocServiceImpl<M extends BaseCrudMapper<E>, E> extends BaseServiceImpl<M, E> implements CocService<E> {

    @Transactional(rollbackFor = Throwable.class)
    public boolean deleteThenAdd(Map deleteParams, List newDatas) {
        if (deleteParams == null || deleteParams.size() == 0)
            return false;

        try {
            this.deleteBy(deleteParams);
            List entityList = new ArrayList();
            for (Object data : newDatas) {
                Object entity = this.getEntityClass().newInstance();
                if (entity instanceof TimeEntity) {
                    ((TimeEntity) entity).setCreateTime(new Timestamp(new Date().getTime()));
                }
                if (data instanceof Map) {
                    BeanUtils.bindProperties(entity, (Map) data);
                } else {
                    BeanUtils.copyProperties(data, entity);
                }
                entityList.add(entity);
            }
            if (entityList.size() > 0)
                return this.saveBatch(entityList);
            return true;
        } catch (Throwable e) {
            throw new CocException(e);
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    public boolean saveAll(List<String> uniqueFields, List newDatas) {
        if (uniqueFields == null || uniqueFields.size() == 0) {
            return false;
        }
        try {
            List entityList = new ArrayList();
            for (Object data : newDatas) {
                Map queryParams = new LinkedHashMap();
                if (data instanceof Map) {
                    for (String fld : uniqueFields) {
                        queryParams.put(fld, ((Map<?, ?>) data).get(fld));
                    }
                } else {
                    for (String fld : uniqueFields) {
                        queryParams.put(fld, BeanUtils.getProperty(data, fld));
                    }
                }
                Object entity = this.getBy(queryParams);
                if (entity == null) {
                    entity = this.getEntityClass().newInstance();
                    if (entity instanceof TimeEntity) {
                        ((TimeEntity) entity).setCreateTime(new Timestamp(new Date().getTime()));
                    }
                }
                if (data instanceof Map) {
                    BeanUtils.bindProperties(entity, (Map) data);
                } else {
                    BeanUtils.copyProperties(data, entity);
                }
                entityList.add(entity);
            }
            if (entityList.size() > 0)
                return this.saveOrUpdateBatch(entityList);
            return true;
        } catch (Throwable e) {
            throw new CocException(e);
        }
    }

    public Object getBy(Map queryParams) {
        if (queryParams == null || queryParams.size() == 0)
            return null;

        CocQueryWrapper queryWrapper = new CocQueryWrapper();
        this.parseSimpleParams(queryWrapper, queryParams);

        return this.getOne(queryWrapper);
    }

    @Transactional(rollbackFor = Throwable.class)
    public boolean deleteBy(Map queryParams) {
        if (queryParams == null || queryParams.size() == 0)
            return false;

        CocQueryWrapper queryWrapper = new CocQueryWrapper();
        this.parseSimpleParams(queryWrapper, queryParams);

        return this.remove(queryWrapper);
    }

    public List queryTree(Map queryParams) {
        List<TreeEntity> list = list(parseQueryWrapper(queryParams));
        List<TreeNode> tree = TreeMaker.makeTree(list);

        return tree;
    }

    @Transactional(rollbackFor = Throwable.class)
    public boolean sort(String prevRecordId, String currRecordId, String nextRecordId) {
        if (!SortEntity.class.isAssignableFrom(this.getEntityClass())) {
            throw new CocException("对[%s]实体数据排序失败！错误原因：[%s]实体类没有实现[SortEntity]接口。");
        }

        // 查找前后记录
        SortEntity prevRecord = null;
        SortEntity nextRecord = null;
        if (org.springframework.util.StringUtils.hasText(prevRecordId)) {
            prevRecord = (SortEntity) this.getById(prevRecordId);
            nextRecord = _getNextRecord(prevRecord);
        }
        if (org.springframework.util.StringUtils.hasText(nextRecordId)) {
            nextRecord = (SortEntity) this.getById(nextRecordId);
            prevRecord = _getPrevRecord(nextRecord);
        }

        /* 获取前记录顺序值 和 后记录顺序值 */
        BigDecimal prevSortValue = null;
        BigDecimal nextSortValue = null;
        if (prevRecord != null) {
            prevSortValue = prevRecord.getSortValue();
        }
        if (nextRecord != null) {
            nextSortValue = nextRecord.getSortValue();
        }

        /*
        计算当前记录的新顺序值：有4中情况
        */
        BigDecimal currSortValue = null;

        // 1. 前后记录均无顺序值：则修改前记录顺序值为当前系统时间，修改后记录顺序值为当前系统时间减1小时
        if (prevSortValue == null && nextSortValue == null) {
            prevSortValue = new BigDecimal(System.currentTimeMillis());
            nextSortValue = prevSortValue.subtract(new BigDecimal(3600000));

            this._updateSortValaue(prevRecordId, prevSortValue);
            this._updateSortValaue(nextRecordId, nextSortValue);
        }

        // 2. 前后记录均有顺序值：新顺序值 = (前记录顺序值 + 后记录顺序值) / 2
        if (prevSortValue != null && nextSortValue != null) {
            currSortValue = prevSortValue.add(nextSortValue).divide(new BigDecimal(2));
        }
        // 3. 仅前记录有顺序值：新顺序值 = 前记录顺序值 - 1小时
        else if (prevSortValue != null) {
            currSortValue = prevSortValue.subtract(new BigDecimal(3600000));
        }
        // 4. 仅后记录有顺序值：新顺序值 = (当前系统时间 + 后记录顺序值) / 2
        else if (nextSortValue != null) {
            currSortValue = new BigDecimal(System.currentTimeMillis()).add(nextSortValue).divide(new BigDecimal(2));
        }

        // 修改当前记录的顺序值以及层级位置
        try {
            SortEntity currRecord = (SortEntity) this.getEntityClass().newInstance();
            currRecord.setSortValue(currSortValue);
            if (currRecord instanceof IdEntity) {
                ((IdEntity) currRecord).setId(Long.parseLong(currRecordId));
            }
            // 修改树形记录为相同层级（即parentId相同）
            if (nextRecord != null) {
                this._setSameParentId(currRecord, nextRecord);
            } else if (prevRecord != null) {
                this._setSameParentId(currRecord, prevRecord);
            }
            return this.updateById((E) currRecord);
        } catch (Throwable e) {
            throw new CocException(e);
        }
    }

    private void _setSameParentId(SortEntity currRecord, SortEntity refRecord) {
        if (currRecord instanceof TreeEntity) {
            Long parentId = ((TreeEntity) refRecord).getParentId();
            if (parentId == null) {
                parentId = 0L;
            }
            ((TreeEntity) currRecord).setParentId(parentId);
        }
    }

    private void _updateSortValaue(String recordId, BigDecimal sortValue) {
        try {
            SortEntity tmpData = (SortEntity) this.getEntityClass().newInstance();
            tmpData.setSortValue(sortValue);
            if (tmpData instanceof IdEntity) {
                ((IdEntity) tmpData).setId(Long.parseLong(recordId));
            }
            this.updateById((E) tmpData);
        } catch (Throwable e) {
            throw new CocException(e);
        }
    }

    /**
     * 查询后记录的前一条记录
     */
    private SortEntity _getPrevRecord(SortEntity nextRecord) {
        CocQueryWrapper queryWrapper = new CocQueryWrapper();
        queryWrapper.orderByAsc("sort_value");
        queryWrapper.gt("sort_value", nextRecord.getSortValue());
        if (TreeEntity.class.isAssignableFrom(this.getEntityClass())) {
            Long parentId = ((TreeEntity) nextRecord).getParentId();
            if (parentId == null || parentId == 0) {
                Consumer<CocQueryWrapper> consumer = qw -> {
                    qw.isNull("parent_id");
                    qw.or();
                    qw.eq("parent_id", 0);
                };
                queryWrapper.and(consumer);
            } else {
                queryWrapper.eq("parent_id", parentId);
            }
        }
        queryWrapper.last("limit 1");

        return (SortEntity) this.getOne(queryWrapper);
    }

    /**
     * 查询前记录的后一条记录
     */
    private SortEntity _getNextRecord(SortEntity prevRecord) {
        CocQueryWrapper queryWrapper = new CocQueryWrapper();
        queryWrapper.orderByDesc("sort_value");
        queryWrapper.lt("sort_value", prevRecord.getSortValue());
        if (TreeEntity.class.isAssignableFrom(this.getEntityClass())) {
            Long parentId = ((TreeEntity) prevRecord).getParentId();
            if (parentId == null || parentId == 0) {
                Consumer<CocQueryWrapper> consumer = qw -> {
                    qw.isNull("parent_id");
                    qw.or();
                    qw.eq("parent_id", 0);
                };
                queryWrapper.and(consumer);
            } else {
                queryWrapper.eq("parent_id", parentId);
            }
        }
        queryWrapper.last("limit 1");

        return (SortEntity) this.getOne(queryWrapper);
    }

    public List queryList(Map queryParams) {
        return this.getViewObjectList(this.parseQueryWrapper(queryParams), null, this.getEntityClass());
    }

    public PageResult queryPageResult(Map queryParams) {
        if (queryParams == null) {
            queryParams = new LinkedHashMap();
        }

        CocQueryWrapper queryWrapper = parseQueryWrapper(queryParams);

        // 解析【查询参数】中的【分页参数】
        Pagination pagination = this.parsePagination(queryParams);
        // 多租环境下第三方API返回的总记录数不正确，此处需重新计算
        int total = this.count(queryWrapper);
        pagination.setTotalCount(total);
        List<E> list = this.getViewObjectList(queryWrapper, pagination, this.getEntityClass());

        PageResult pageResult = new PageResult();
        pageResult.setPageIndex(pagination.getPageIndex());
        pageResult.setPageSize(pagination.getPageSize());
        pageResult.setTotalPages(pagination.getTotalPage());
        pageResult.setTotalRecords(total);
        pageResult.setResult(list);

        return pageResult;

    }

    /**
     * 解析【查询参数】中的【分页参数】
     */
    protected Pagination parsePagination(Map queryParams) {
        int pageIndex = 1;
        int pageSize = 20;
        if (queryParams.get("pageIndex") != null) {
            pageIndex = (int) queryParams.get("pageIndex");
        }
        if (queryParams.get("pageSize") != null) {
            pageSize = (int) queryParams.get("pageSize");
        }

        Pagination pagination = new Pagination(pageIndex);
        pagination.setPageSize(pageSize);

        return pagination;
    }

    /**parseQueryWrapper
     * 解析【查询参数】中的【查询条件】和【排序】
     */
    protected CocQueryWrapper parseQueryWrapper(Map queryParams) {
        if (queryParams == null) {
            queryParams = new LinkedHashMap();
        }

        CocQueryWrapper queryWrapper = new CocQueryWrapper();
        this.parseSortParams(queryWrapper, queryParams);
        this.parseSimpleParams(queryWrapper, queryParams);

        return queryWrapper;
    }

    /**
     * 解析【查询参数】中的【简单参数】
     */
    protected void parseSimpleParams(CocQueryWrapper queryWrapper, Map queryParams) {
        Iterator<String> keys = queryParams.keySet().iterator();
        while (keys.hasNext()) {
            String key = keys.next();
            Object val = queryParams.get(key);
            // 排除分页排序参数
            if ("sortField".equals(key)
                    || "sortOrder".equals(key)
                    || "pageIndex".equals(key)
                    || "pageSize".equals(key)
            ) {
                continue;
            }
            // 关键字查询
            if ("keywords".equals(key)) {
                queryWrapper.like("name", val);
                continue;
            }
            // 按字段名查询
            if (val instanceof List) {
                queryWrapper.regexp(StringUtils.camelToUnderline(key), ObjectUtils.join((List) val, "|"));
            } else {
                queryWrapper.eq(StringUtils.camelToUnderline(key), val);
            }

        }
    }

    /**
     * 解析【查询参数】中的【排序参数】
     */
    protected void parseSortParams(CocQueryWrapper queryWrapper, Map queryParams) {
        String sortField = (String) queryParams.get("sortField");
        String sortOrder = (String) queryParams.get("sortOrder");
        if (sortField != null && sortField.length() > 0) {
            sortField = StringUtils.camelToUnderline(sortField);
            if ("desc".equals(sortOrder)) {
                queryWrapper.orderByDesc(sortField);
            } else {
                queryWrapper.orderByAsc(sortField);
            }
        }
        if (SortEntity.class.isAssignableFrom(this.getEntityClass())) {
            queryWrapper.orderByDesc("sort_value");
        }
        queryWrapper.orderByDesc("create_time");
    }
}
